#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <strings.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "comm.h"
#include "config.h"
#include "manage.h"
#include "main.h"

const char *cmdtip[] = {
	"Error Command!\n",
	"Success!\n",
	"spider$ ",
	"reload conf file failed!\nRolled back!\n",
	"exit\n",
	"reload\nexit\nsuicide\ncrawl regular\n",
	"suicide\n"
};

static void cmd(int sock, const MANAGE_TH_ARG *arg)
{
	char buf[512], cmdstr[512];
	int len, err = 0;
	CONF conf_tmp;
	char **argv = NULL;

	memset(cmdstr, 0, sizeof cmdstr);
	while (1) {
		len = read(sock, buf, sizeof buf - 1);
		if (len < 0) {
			perror("read");
			break;
		}
		if (len == 0) {
			printf("");
			break;
		}
		buf[len] = '\0';
		strncat(cmdstr, buf, sizeof cmdstr - strlen(cmdstr) - 1);
		if ((strchr(cmdstr, '\n') != NULL) || (sizeof cmdstr == (strlen(cmdstr) + 1))) {
			argv = NULL;
			if (strchr(cmdstr, '\n') != NULL)
				*strchr(cmdstr, '\n') = '\0';
			if (cmdstr[strlen(cmdstr) - 1] == '\r')
				cmdstr[strlen(cmdstr) - 1] = '\0';
			char *p = cmdstr;
			while (*p) {
				if (isspace(*p)) {
					*p = '\0';
					p++;
					break;
				}
				p++;
			}
			while (*p && isspace(*p)) p++;
			int n = 1;
			char *start = p;
			while (*p) {
				if (isspace(*p)) {
					*p = '\0';
					argv = (char **)realloc(argv, sizeof(char *) * (n + 1));
					argv[n - 1] = strdup(start);
					argv[n] = NULL;
					n++;
					p++;
					while (*p && isspace(*p)) p++;
					start = p;
				}
			}
			if (strcmp(cmdstr, "reload") == 0) {
				memcpy(&conf_tmp, &conf, sizeof conf);
				pthread_rwlock_wrlock(&conf_lock);
				int ret = init(arg->conf_file);
				pthread_rwlock_unlock(&conf_lock);
				if (ret != 0) {
					memcpy(&conf, &conf_tmp, sizeof conf);
					Write(sock, cmdtip[3], strlen(cmdtip[3]), &err);
					if (err == -1) {
						perror("write");
						break;
					}
				}
				else {
					destroy_conf(&conf_tmp);
					Write(sock, cmdtip[1], strlen(cmdtip[1]), &err);
					if (err == -1) {
						perror("write");
						break;
					}
				}
			}
			else if (strcmp(cmdstr, "exit") == 0) {
				Write(sock, cmdtip[4], strlen(cmdtip[4]), &err);
				if (err == -1) {
					perror("write");
					break;
				}
				break;
			}
			else if (strcmp(cmdstr, "suicide") == 0) {
				Write(sock, cmdtip[6], strlen(cmdtip[6]), &err);
				if (err == -1) {
					perror("write");
					break;
				}
				pthread_exit((void *)1);
			}
			else if (strcmp(cmdstr, "crawl") == 0) {
				pthread_rwlock_wrlock(&conf_lock);
				memset(conf.regular, 0, sizeof(conf.regular));
				strncpy(conf.regular, argv[0], sizeof(conf.regular) - 1);
				pthread_rwlock_unlock(&conf_lock);
			}
			else if (strcmp(cmdstr, "help") == 0) {
				Write(sock, cmdtip[5], strlen(cmdtip[5]), &err);
				if (err == -1) {
					perror("write");
					break;
				}
			}
			else {
				Write(sock, cmdtip[0], strlen(cmdtip[0]), &err);
				if (err == -1) {
					perror("write");
					break;
				}
			}
			memset(cmdstr, 0, sizeof cmdstr);
			if (argv) {
				for (int i = 0; argv[i]; i++)
					free(argv[i]);
				free(argv);
			}
			Write(sock, cmdtip[2], strlen(cmdtip[2]), &err);
			if (err == -1) {
				perror("write");
			}
		}
	}
}

void *manage_thread(void *conf_arg)
{
	int sockfd, connfd, err = 0;
	struct sockaddr_in servaddr, cliaddr;
	socklen_t clilen;

	if (conf_arg == NULL)
		return NULL;
	MANAGE_TH_ARG *arg = (MANAGE_TH_ARG *)conf_arg;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1) {
		perror("socket");
		return NULL;
	}
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(conf.manage.port);
	memcpy(&servaddr.sin_addr.s_addr, &conf.manage.ip, sizeof(conf.manage.ip));
	int flag = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&flag, sizeof flag);
	if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
		perror("bind");
		close(sockfd);
		return NULL;
	}
	if (listen(sockfd, 10) < 0) {
		perror("listen");
		close(sockfd);
		return NULL;
	}

	while (1) {
		clilen = sizeof cliaddr;
		connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
		if (connfd < 0) {
			perror("accept");
			close(sockfd);
			return NULL;
		}
		Write(connfd, cmdtip[2], strlen(cmdtip[2]), &err);
		if (err == -1) {
			perror("write");
			close(connfd);
			continue;
		}
		cmd(connfd, arg);
		close(connfd);
	}

	return (void *)1;
}
